package edu.zju.gis.dldsj.controller;

import edu.zju.gis.dldsj.common.Result;
import edu.zju.gis.dldsj.constant.CodeConstants;
import edu.zju.gis.dldsj.constant.FunctionStatus;
import edu.zju.gis.dldsj.constant.ModelStatus;
import edu.zju.gis.dldsj.entity.*;
import edu.zju.gis.dldsj.entity.vo.UserDataWithVisit;
import edu.zju.gis.dldsj.service.*;
import edu.zju.gis.dldsj.utils.FileUtil;
import edu.zju.gis.dldsj.utils.GeometryUtil;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.time.Instant;
import java.util.*;

/**
 * @author yanlo yanlong_lee@qq.com
 * @version 1.0 2018/08/09
 */
@Slf4j
@CrossOrigin
@Controller
@RequestMapping("/user")
public class UserController {
    @Autowired
    private RoleService roleService;
    @Autowired
    private UserService userService;
    @Autowired
    private UserDataService dataService;
    @Autowired
    private UserDataFieldService fieldService;
    @Autowired
    private UserModelService modelService;
    @Autowired
    private UserFavoriteService favoriteService;
    @Autowired
    private TaskRecordService taskRecordService;

    @RequestMapping(value = "/register", method = RequestMethod.POST)
    @ResponseBody
    public String register(@SessionAttribute("roleId") String roleId, @RequestBody String requestBody) {
        Result<String> result = new Result<>();
        Role currentRole = roleService.select(roleId);
        if (currentRole == null || !currentRole.getIsAdministrator().equals("true")
                || !currentRole.getStatus().equals(FunctionStatus.NORMAL.name())) {
            result.setCode(CodeConstants.SSO_PERMISSION_ERROR);
            result.setMessage("失败，当前用户无该项权限");
            return result.toString();
        }
        try {
            JSONObject json = new JSONObject(requestBody);
            User user = new User();
            user.setRoleId(json.getString("roleId"));
            user.setName(json.getString("name"));
            user.setFullName(json.optString("fullName"));
            user.setPassword(json.getString("password"));
            user.setPsdPrompt(json.optString("psd_prompt"));
            user.setSex(json.getString("sex"));
            user.setDescription(json.optString("description"));
            user.setPhone(json.optString("phone"));
            user.setEmail(json.optString("email"));
            user.setOrganization(json.optString("organization"));
            user.setDepartment(json.optString("department"));
            user.setId(UUID.randomUUID().toString());
            user.setRegistrationTime(Date.from(Instant.now()));
            user.setStatus(FunctionStatus.NORMAL.name());
            userService.insert(user);

            String[] modelIds = json.optString("modelId").split(",");
            List<UserModel> models = new ArrayList<>();
            for (String modelId : modelIds) {
                UserModel userModel = new UserModel();
                userModel.setId(UUID.randomUUID().toString());
                userModel.setUserId(user.getId());
                userModel.setModelId(modelId);
                userModel.setRegistrationTime(user.getRegistrationTime());
                userModel.setStatus(ModelStatus.NORMAL.name());
                models.add(userModel);
            }
            modelService.insert(models);
            result.setCode(CodeConstants.SUCCESS).setBody("success");
        } catch (RuntimeException e) {
            log.error("用户添加失败", e);
            result.setCode(CodeConstants.VALIDATE_ERROR).setMessage("用户注册失败：" + e.getMessage());
        }
        return result.toString();
    }

    @RequestMapping(value = "/login", method = RequestMethod.POST)
    @ResponseBody
    public String login(String name, String pass, HttpSession session) {
        Result<String> result = new Result<>();
        User user = userService.select(name);
        if (user == null) {
            result.setCode(CodeConstants.USER_NOT_EXIST);
            result.setMessage("该用户名不存在，请注册");
        } else {
            if (user.getPassword().equals(pass)) {
                result.setCode(CodeConstants.SUCCESS);
                result.setBody("success");
                session.setAttribute("userId", user.getId());
                session.setAttribute("userName", user.getName());
                session.setAttribute("fullName", user.getFullName());
                session.setAttribute("roleId", user.getRoleId());
            } else {
                result.setCode(CodeConstants.USERNAME_OR_PASSWORD_ERROR);
                result.setMessage("用户名或密码错误");
            }
        }
        return result.toString();
    }

    @RequestMapping(value = "/logout", method = RequestMethod.GET)
    public String logout(HttpSession session) {
        session.removeAttribute("userId");
        session.removeAttribute("userName");
        session.removeAttribute("fullName");
        session.removeAttribute("roleId");
        return "redirect:/user/login";
    }

    @RequestMapping(value = "/data", method = RequestMethod.GET)
    @ResponseBody
    public String getDataList(@SessionAttribute("userId") String userId, Integer offset, int size) {
        Result<Map<String, Object>> result = new Result<>();
        if (offset == null)
            offset = 0;
        try {
            int totalSize = dataService.getTotalSize(userId);
            Map<String, Object> body = new HashMap<>();
            body.put("totalSize", totalSize);
            if (totalSize > 0) {
                List<UserDataWithVisit> dataWithVisits = dataService.getByVisitDesc(userId, offset, size);
                body.put("data", dataWithVisits);
            } else {
                body.put("data", Collections.emptyList());
            }
            result.setCode(CodeConstants.SUCCESS).setBody(body);
        } catch (RuntimeException e) {
            log.error("数据列表拉取失败", e);
            result.setCode(CodeConstants.SERVICE_ERROR).setMessage("数据列表拉取失败" + e.getMessage());
        }
        return result.toString();
    }

    @RequestMapping(value = "/data/field/{dataId}", method = RequestMethod.GET)
    @ResponseBody
    public String getFieldInfo(@PathVariable String dataId) {
        Result<List<DataField>> result = new Result<>();
        try {
            List<DataField> fields = fieldService.getByDataId(dataId);
            result.setCode(CodeConstants.SUCCESS).setBody(fields);
        } catch (Exception e) {
            log.error("数据`" + dataId + "`的字段信息获取失败", e);
            result.setCode(CodeConstants.DAO_ERROR).setMessage("字段信息读取失败：" + e.getMessage());
        }
        return result.toString();
    }

    @RequestMapping(value = "/data/preview/{dataId}", method = RequestMethod.GET)
    @ResponseBody
    public String preview(@PathVariable String dataId, Integer offset, int size) {
        Result<List<String[]>> result = new Result<>();
        try {
            UserData userData = dataService.select(dataId);
            if (userData == null)
                return result.setCode(CodeConstants.SERVICE_ERROR).setMessage("数据`" + dataId + "`不存在或已被删除").toString();
            result.setCode(CodeConstants.SUCCESS).setBody(getDataAsArray(userData, size));
        } catch (Exception e) {
            log.error("我的数据`" + dataId + "`读取失败", e);
            result.setCode(CodeConstants.SERVICE_ERROR).setMessage("我的数据`" + dataId + "`读取失败：" + e.getMessage());
        }
        return result.toString();
    }

    private static final List<String> GEOM_FIELDS = Arrays.asList("lon", "lat", "start_lon", "start_lat", "end_lon", "end_lat", "geom");

    @RequestMapping(value = "/data/preview/{dataId}/geojson", method = RequestMethod.GET)
    @ResponseBody
    public String previewAsGeoJSON(@PathVariable String dataId, Integer offset, int size) {
        Result<JSONObject> result = new Result<>();
        try {
            List<DataField> dataFields = fieldService.getByDataId(dataId);
            boolean visible = dataFields.stream().anyMatch(field -> GEOM_FIELDS.contains(field.getFieldName()));
            if (!visible)
                result.setCode(CodeConstants.SERVICE_ERROR).setMessage("数据`" + dataId + "`不支持空间化");
            else {
                UserData userData = dataService.select(dataId);
                if (userData == null)
                    result.setCode(CodeConstants.SERVICE_ERROR).setMessage("数据`" + dataId + "`不存在或已被删除");
                else {
                    JSONArray jsons = getDataAsJSONArray(userData, size);
                    JSONObject geojson = GeometryUtil.toGeoJSON(jsons, userData.getName());
                    result.setCode(CodeConstants.SUCCESS).setBody(geojson);
                }
            }
        } catch (Exception e) {
            log.error("数据`" + dataId + "`空间化失败", e);
            result.setCode(CodeConstants.SERVICE_ERROR).setMessage("数据`" + dataId + "`空间化失败：" + e.getMessage());
        }
        return result.toString();
    }

    /**
     * @param dataType 数据所属的表。公共数据-meta_storage，用户数据-sys_user_data
     */
    @RequestMapping(value = "/favorite/add/{dataId}", method = RequestMethod.POST)
    @ResponseBody
    public String addFavorite(@SessionAttribute("userId") String userId, @PathVariable String dataId, String dataType) {
        Result<String> result = new Result<>();
        UserFavorite userFavorite = new UserFavorite();
        userFavorite.setId(UUID.randomUUID().toString());
        userFavorite.setDataId(dataId);
        userFavorite.setUserId(userId);
        userFavorite.setDataTable(dataType);
        userFavorite.setCreateTime(Date.from(Instant.now()));
        try {
            favoriteService.insert(userFavorite);
            result.setCode(CodeConstants.SUCCESS);
            result.setBody("success");
        } catch (RuntimeException e) {
            log.error("数据收藏失败", e);
            result.setCode(CodeConstants.SERVICE_ERROR).setMessage("数据收藏失败：" + e.getMessage());
        }
        return result.toString();
    }

    @RequestMapping(value = "/favorite/delete/{dataId}", method = RequestMethod.DELETE)
    @ResponseBody
    public String deleteFavorite(@SessionAttribute("userId") String userId, @PathVariable String dataId, String dataType) {
        Result<String> result = new Result<>();
        try {
            favoriteService.deleteByDataId(userId, dataId, dataType);
            result.setCode(CodeConstants.SUCCESS);
            result.setBody("success");
        } catch (RuntimeException e) {
            log.error("数据收藏删除失败", e);
            result.setCode(CodeConstants.SERVICE_ERROR).setMessage("数据收藏删除失败：" + e.getMessage());
        }
        return result.toString();
    }

    private List<String[]> getDataAsArray(UserData userData, Integer size) throws IOException {
        TaskRecord taskRecord = taskRecordService.select(userData.getName());
        boolean skipFirstLine = taskRecord != null && taskRecord.getToolId().startsWith("edu.zju.gis.dldsj.stat");
        List<String> lines = FileUtil.readByLine(userData.getStorageLocation(), size, skipFirstLine);
        List<DataField> dataFields = fieldService.getByDataId(userData.getId());
        String[] fields = dataFields.stream().map(DataField::getFieldName).toArray(String[]::new);
        boolean isCsv = lines.stream().limit(20).noneMatch(s -> s.contains("\t"));
        List<String[]> data = new ArrayList<>();
        data.add(fields);
        lines.forEach(s -> data.add(s.split(isCsv ? "," : "\t", -1)));
        return data;
    }

    private JSONArray getDataAsJSONArray(UserData userData, Integer size) throws IOException {
        TaskRecord taskRecord = taskRecordService.select(userData.getName());
        boolean skipFirstLine = taskRecord != null && taskRecord.getToolId().startsWith("edu.zju.gis.dldsj.stat");
        List<String> lines = FileUtil.readByLine(userData.getStorageLocation(), size, skipFirstLine);
        List<DataField> dataFields = fieldService.getByDataId(userData.getId());
        String[] fields = dataFields.stream().map(DataField::getFieldName).toArray(String[]::new);
        JSONArray jsons = new JSONArray();
        boolean isCsv = lines.stream().limit(20).noneMatch(s -> s.contains("\t"));
        lines.forEach(s -> {
            String[] values = s.split(isCsv ? "," : "\t", -1);
            JSONObject json = new JSONObject();
            for (int i = 0; i < fields.length; i++)
                json.put(fields[i], values[i]);
            jsons.put(json);
        });
        return jsons;
    }

}
